﻿using ModelLayer.Graph;
using DataRepositoryLayer.Classes;
using DataRepositoryLayer.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ModelLayer;
using System.Data;
using ModelLayer.DTO;
using DataAccessLayer;
using DataRepositoryLayer.Classes;
using System.Threading;
using System.Diagnostics;

namespace BusinessLogicLayer
{
    public class ReservationManager
    {
        public async Task<StationNetworkGraph> GetStationGraph(double[] startPointCoords, double[] endPointCoords,
            BatteryType requestedBatteryType, string sessionToken)
        {
            IList<Station> stations = null;
            BatteryType desiredType = null;
            using(IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository accountRep = new UserAccountRepository(context);
                if (accountRep.CheckSessionToken(sessionToken) == null)
                {
                    return null;
                }

                IBatteryTypeRepository typeRepository = new BatteryTypeRepository(context);
                desiredType = typeRepository.GetById(requestedBatteryType.id);
                if(desiredType == null)
                {
                    return null;
                }
                
                IStationRepository stationRepository = new StationRepository(context);
                stations = stationRepository.GetAllForGraph(); 
            }

            
            GraphCreator gc = new GraphCreator();
            //return null;
            return await gc.CreateGraph(stations, startPointCoords, endPointCoords, desiredType);
        }

        public IList<BatteryType> GetAllBatteryTypes()
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IBatteryTypeRepository batteryTypeRepository = new BatteryTypeRepository(context);
                return batteryTypeRepository.GetAllForManagementSystem();
            }
        }

        public bool TryToLendBattery(Battery battery)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                context.BeginTransaction();
                IBatteryTypeRepository typeRepository = new BatteryTypeRepository(context);
                BatteryType desiredType = typeRepository.GetById(battery.battery_type_id);//Take the type in the db, cause the one sent from the client might be old
                if (desiredType == null)    //Make sure that the type still exists
                {
                    return false;
                }
                IBatteryRepository batRep = new BatteryRepository(context);
                Battery batToReserve = batRep.GetByIdForReservation(battery.id);
                if (batToReserve == null)    //Make sure that the battery still exists
                {
                    return false;
                }

                IBatteryReservationRepository batResRep = new BatteryReservationRepository(context);
                batResRep.TryToReserveSingle(batToReserve, desiredType);
                context.Commit();
                context.CommitTransaction();
            }
            throw new NotImplementedException();
        }

        public bool TryToReserveRoute(IList<PointReservationDTO> pointsOnRoute, BatteryType type, string sessionToken)
        {
            foreach(PointReservationDTO p in pointsOnRoute)
            {
                Debug.WriteLine(p.StationId + " " + p.Distance + " " + p.ReservationTime);
            }
            using (IUnitOfWork context = new UnitOfWork())
            {
                context.BeginTransaction();
                IUserAccountRepository userAccountRepository = new UserAccountRepository(context);
                UserAccount accountToConnect = userAccountRepository.CheckSessionToken(sessionToken);  //Take the up to date accout  
                if (accountToConnect == null)   //Make sure the account still exists
                {
                    return false;
                }

                IBatteryTypeRepository typeRepository = new BatteryTypeRepository(context);
                BatteryType desiredType = typeRepository.GetById(type.id);//Take the type in the db, cause the one sent from the client might be old
                if (desiredType == null)    //Make sure that the type still exists
                {
                    return false;
                }

                IBatteryRepository batteryRepository = new BatteryRepository(context);
                IList<Battery> possibleBatteries = batteryRepository.GetPossibleBatteriesToReserve(pointsOnRoute, desiredType);

                IBatteryReservationRepository batteryReservationRepository = new BatteryReservationRepository(context);
                IList<BatteryToReserveDTO> batteriesToReserve =
                    batteryReservationRepository.GetReservationsOnRoute(pointsOnRoute, possibleBatteries, desiredType);
                if (batteriesToReserve == null)
                {
                    return false;   //in case we couldn't find suitable batteries
                }

                IRouteReservationRepository routeRepository = new RouteReservationRepository(context);
                routeRepository.ReserveRoute(batteriesToReserve, desiredType, accountToConnect);
                context.Commit();
                context.CommitTransaction(); // transaction will only be comitted when we are done with this part
                return true;
            }
        }

        public IList<BatteryReservation> GetCurrentReservations()
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IBatteryReservationRepository resRep = new BatteryReservationRepository(context);
                return resRep.GetCurrentReservations();
            }
        }

        public IList<BatteryReservation> GetCurrentReservationsOnStation(Station station)
        {
            using (IUnitOfWork context = new UnitOfWork())
            {
                IStationRepository statRep = new StationRepository(context);
                Station dbStation = statRep.GetByIdWithBatteries(station.id);
                if (dbStation == null)
                {
                    return null;
                }
                IBatteryReservationRepository resRep = new BatteryReservationRepository(context);
                return resRep.GetCurrentReservations(dbStation);
            }
        }

        public IList<RouteReservation> GetUserReservations(string sessionToken)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository accountRep = new UserAccountRepository(context);
                UserAccount accountOfCaller = accountRep.CheckSessionToken(sessionToken);
                if(accountOfCaller == null)
                {
                    return null;
                }

                IRouteReservationRepository reservationRep = new RouteReservationRepository(context);
                return reservationRep.GetUserReservations(accountOfCaller);
            }
        }

        public void UpdateReservation(BatteryReservation reservation)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IBatteryReservationRepository resRep = new BatteryReservationRepository(context);
                resRep.Update(reservation);
                context.Commit();
            }
        }


        public bool CancelReservation(RouteReservation reservation, string sessionToken)
        {
            using(IUnitOfWork context = new UnitOfWork())
            {
                IUserAccountRepository accountRepo = new UserAccountRepository(context);
                if (accountRepo.CheckSessionToken(sessionToken) == null)
                {
                    return false;
                }
                Console.WriteLine("Thread: " + Thread.CurrentThread.Name + " wants to remove reservation.");
                IRouteReservationRepository routeRepository = new RouteReservationRepository(context);
                routeRepository.Remove(reservation);
                Console.WriteLine("Thread: " + Thread.CurrentThread.Name + " removed reservation.");
                context.Commit();
                Console.WriteLine("Thread: " + Thread.CurrentThread.Name + " finished.");
                return true;
            }
        }


        public void InsertStuff()
        {
            using (IUnitOfWork context = new UnitOfWork())
            {

                UserAccount ua = new UserAccount() { lname = "aaaa", fname = "eeee", email = "efkekog@gmail.com", phone_nr = "12-34-56-78", registration_date = new DateTime(), username = "bob", pass = "aaaa" };
                IStationRepository statRep = new StationRepository(context);
                IList<Station> stations = statRep.GetAll();
                IList<Battery> batteries = new List<Battery>();

                foreach (Station s in stations)
                {
                    Battery b = new Battery() { station_id = s.id, battery_type_id = 2, charge_time = DateTime.Now };
                    batteries.Add(b);
                }
                //statRep.AddMultiple(stations.ToArray());
                //IUserAccountRepository rep = new UserAccountRepository(context);
                //rep.Add(ua);
                
                IBatteryRepository batRep = new BatteryRepository(context);
                batRep.AddMultiple(batteries.ToArray());

                context.Commit();
            }
        }

        public void InsertStuff2()
        {
            using (IUnitOfWork context = new UnitOfWork())
            {

                IStationRepository statRep = new StationRepository(context);
                IList<Station> stations = statRep.GetAll();
                IBatteryTypeRepository typeRep = new BatteryTypeRepository(context);
               
                IList<BatteryType> types = typeRep.GetAll();
                IList<Pricing> pricings = new List<Pricing>();
                Random rand = new Random();
                foreach (Station s in stations)
                {
                    foreach (BatteryType type in types)
                    {
                        Pricing p = new Pricing() { battery_type_id = type.id, station_id = s.id, price = 25 + rand.Next(50) };
                        pricings.Add(p);
                    }
                }
                IPricingRepository pricingRep = new PricingRepository(context);
                pricingRep.AddMultiple(pricings.ToArray());

                context.Commit();
            }
        }



        
    }

}
